.align 2 	#对齐伪指令，必须从一个能被2整除的地址开始为下面的内存变量分配空间
switch_to:

# 内存模型: stack
	pushl %ebp
	movl %esp,%ebp
	pushl %ecx
	pushl %ebx
	pushl %eax
	movl 8(%ebp),%ebx   # 调用switch_to的第一个参数，即pnext——目标进程的PCB
	cmpl %ebx, current  # 和 current 做一个比较, current指向当前进程的PCB
	je 1f               # 相等就啥也不用做，跳转到“1：”直接恢复保存的寄存器即可
	
# 不相等，开始进程切换
# 先切换 PCB
	movl %ebx,%eax 		# eax指向目标进程的PCB
	xchgl %eax,current 	# current指向目标进程的PCB, eax指向当前进程的PCB

# TSS 中的内核指针的重写
	movl tss, %ecx 				# ecx指向当前进程的TSS
	addl $4096, %ebx 			# ebx指向的是目标进程的PCB，+ 4096后指向目标进程的内核栈
	movl %ebx, ESP0(%ecx) 		# TSS的esp0指向目标进程的内核栈

# 切换内核栈栈顶指针（切换当前的内核栈为目标内核栈）；当然要先保存被切换进程的esp
	movl %esp, KERNEL_STACK(%eax) 	# 把当前进程的esp保存到其PCB中
	movl 8(%ebp),%ebx 				# 再一次把目标进程的PCB存储到ebx中
	movl KERNEL_STACK(%ebx),%esp 	# 把目标进程PCB中的内核栈基址存储到esp中

# 切换LDT
	movl 12(%ebp),%ecx 	# 把_LDT(next)存储到ecx中
	lldt %cx 			# 修改LDTR寄存器后，目标进程在执行用户态程序时使用的映射表就是自己的LDT表，实现了地址空间的分离。

# 通过 ret 完成 PC 的切换

# 切换 LDT 之后，更新 fs，指向目标进程的用户态内存
	movl $0x17, %ecx  	# 用户空间数据段选择符为0x17
	mov %cx, %fs 

# 和后面的 clts 配合来处理协处理器，由于和主题关系不大，此处不做论述
	cmpl %eax, last_task_used_math
	jne 1f
	clts

1:
	popl %eax
	popl %ebx
	popl %ecx
	popl %ebp

ret
